package dbmanager

import (
	"reflect"
	"fmt"
	"sort"
	"github.com/astaxie/beego/orm"
	"cloud_worm/util/umodels/mysqldb"
	"cloud_worm/util/umodels/mysqldb/dbdata"
)

type Application struct {
	Id            	int64
	Name          	string 		// 应用名

	Created 		int64
	Updated 		int64
	Status  		int
}

func (wa *Application) TableName() string {
	return reflect.TypeOf(*wa).Name()
}

func (wa *Application) Read(fields ...string) error {
	err := orm.NewOrm().Read(wa, fields...)
	if err != nil {
		// todo: 具体错误处理，参考(https://beego.me/docs/mvc/model/object.md)
		// orm.ErrNoRows
		return fmt.Errorf("%s, %s", wa.TableName(), err.Error())
	}
	return nil
}

func (wa *Application) Update(fields ...string) error {
	_, err := orm.NewOrm().Update(wa, fields...)
	if err != nil {
		return fmt.Errorf("%s, %s", wa.TableName(), err.Error())
	}
	return nil
}

func (wa *Application) Insert() (int64, error) {
	id, err := orm.NewOrm().Insert(wa)
	if err != nil {
		return 0, fmt.Errorf("%s, %s", wa.TableName(), err.Error())
	}
	return id, nil
}

func init() {
	orm.RegisterModel(new(Application))
}

func (wa *Application) Count(status int, name string) (int, error) {
	qs := orm.NewOrm().QueryTable(wa)

	if name != mysqldb.SelectStrAll {
		// 设置name条件
		qs = qs.Filter("name__istartswith", name)
	}

	if status != mysqldb.StatusAll {
		qs = qs.Filter("status", status)
	}

	count, err := qs.Count()
	if err != nil {
		return 0, err
	}
	return int(count), nil
}

func (wa *Application) Select(offset int, limit int, status int, name string) ([]*Application, error) {
	var results []*Application

	qs := orm.NewOrm().QueryTable(wa).Offset(offset).Limit(limit)

	if name != mysqldb.SelectStrAll {
		// 设置name条件
		qs = qs.Filter("name__istartswith", name)
	}

	if status != mysqldb.StatusAll {
		qs = qs.Filter("status", status)
	}

	_, err := qs.All(&results)
	if err != nil {
		return nil, err
	}

	return results, nil
}

// @Description: 返回应用的详细信息
type ApplyDetail struct {
	Application
	Alert       int
	MaxScore    int
	MinScore    int
	AverScore   int
}

// 基于指定字段读取
func (wa *Application) ApplicationDetailById() (*ApplyDetail, error) {
	// 根据id取application的应用信息
	err := wa.Read("id")
	if err != nil {
		return nil, err
	}

	// 拼装application slice
	var waps []*Application
	waps = append(waps, wa)

	// 获取评分信息
	scores, err := wa.getApplicationScore(waps)
	if err != nil {
		return nil, err
	}
	alerts, err := wa.getMachineAlert(scores)
	if err != nil {
		return nil, err
	}

	var apps []*ApplyDetail
	for _, item := range waps {
		a := &ApplyDetail{
			Application: *item,
		}

		// 计算alert
		var allAlert int
		for _, tmp := range alerts[item.Id] {
			allAlert = tmp + allAlert
		}
		a.Alert = allAlert

		// 计算score值
		var scoreForApp []int
		var scoreSum int
		for _, tmp := range scores[item.Id] {
			scoreForApp = append(scoreForApp, tmp)
			scoreSum = scoreSum + tmp
		}
		sort.Ints(scoreForApp)

		if len(scoreForApp) != 0 {
			a.MinScore = scoreForApp[0]
			a.MaxScore = scoreForApp[len(scoreForApp) - 1]
			a.MaxScore = scoreSum/len(scoreForApp)
		} else {
			a.MinScore = 0
			a.MaxScore = 0
			a.MaxScore = 0
		}

		apps = append(apps, a)
	}

	return apps[0], nil
}

/**
 @Description：基于指定的machines得到对应的alert信息
 @Param:
 @Return：
 */
func (wa *Application) Application(offset int, limit int, status int, name string) ([]*ApplyDetail, error) {
	waps, err := wa.Select(offset, limit, status, name)
	if err != nil {
		return nil, err
	}

	scores, err := wa.getApplicationScore(waps)
	if err != nil {
		return nil, err
	}
	alerts, err := wa.getMachineAlert(scores)
	if err != nil {
		return nil, err
	}

	var apps []*ApplyDetail
	for _, item := range waps {
		a := &ApplyDetail{
			Application: *item,
		}

		// 计算alert
		var allAlert int
		for _, tmp := range alerts[item.Id] {
			allAlert = tmp + allAlert
		}
		a.Alert = allAlert

		// 计算score值
		var scoreForApp []int
		var scoreSum int
		for _, tmp := range scores[item.Id] {
			scoreForApp = append(scoreForApp, tmp)
			scoreSum = scoreSum + tmp
		}
		sort.Ints(scoreForApp)

		if len(scoreForApp) != 0 {
			a.MinScore = scoreForApp[0]
			a.MaxScore = scoreForApp[len(scoreForApp) - 1]
			a.MaxScore = scoreSum/len(scoreForApp)
		} else {
			a.MinScore = 0
			a.MaxScore = 0
			a.MaxScore = 0
		}

		apps = append(apps, a)
	}

	return apps, nil
}

/**
 @Description：基于ip查询alert信息
 @Param:
 @Return：
	map[int]map[string]int              app id | ip | alert count
 */
func (wa *Application) getMachineAlert(am map[int64]map[string]int) (map[int64]map[string]int, error) {
	// 获取出所有ip的所有alert count信息
	qb, err := orm.NewQueryBuilder("mysql")
	if err != nil {
		return nil, err
	}

	ma := &dbdata.WeaponAlertCount{}
	ipField := fmt.Sprintf("%s.%s", ma.TableName(), "ip")
	optionFailedField := fmt.Sprintf("%s.%s", ma.TableName(), "option_failed")
	qb = qb.Select(ipField, optionFailedField).From(ma.TableName()).Where(mysqldb.SelectCondFalse)

	mapAlert := make(map[int64]map[string]int)
	for appID, v := range am {
		_, exist := mapAlert[appID]
		if exist == false {
			mapAlert[appID] = make(map[string]int)
		}

		for ip, _ := range v {
			mapAlert[appID][ip] = 0
			qb = qb.Or(fmt.Sprintf("ip='%s'", ip))
		}
	}
	sql := qb.String()

	type AlertInfo struct {
		Ip              string
		OptionFailed    int
	}
	var alerts []*AlertInfo
	_, err = orm.NewOrm().Raw(sql).QueryRows(&alerts)
	if err != nil {
		return nil, err
	}

	// 分析alert count
	ipAlert := make(map[string]int)
	for _, item := range alerts {
		_, exist := ipAlert[item.Ip]
		if exist == false {
			ipAlert[item.Ip] = item.OptionFailed
		} else {
			ipAlert[item.Ip] = ipAlert[item.Ip] + item.OptionFailed
		}
	}

	// 将结果带出
	for appID, v := range mapAlert {
		for ip, _ := range v {
			aCount, exist := ipAlert[ip]
			if exist == true {
				mapAlert[appID][ip] = aCount
			}
		}
	}

	return mapAlert, nil
}

/**
 @Description：基于指定的application得到对应的score信息
 @Param:
 @Return：
	map[int]map[string]int          app id | ip | score
 */
func (wa *Application) getApplicationScore(waps []*Application) (map[int64]map[string]int, error) {
	qb, err := orm.NewQueryBuilder("mysql")
	if err != nil {
		return nil, err
	}

	// 基于应用找到machine
	m := &Machine{}
	appIdField := fmt.Sprintf("%s.%s", m.TableName(), "apply_id")
	scoreField := fmt.Sprintf("%s.%s", m.TableName(), "score")
	ipField := fmt.Sprintf("%s.%s", m.TableName(), "ip")
	qb = qb.Select(appIdField, ipField, scoreField).From(m.TableName()).Where(mysqldb.SelectCondFalse)
	for _, tmp := range waps {
		qb = qb.Or(fmt.Sprintf("apply_id=%d", tmp.Id))
	}
	sql := qb.String()

	type ScoreInfo struct {
		ApplyId     int64
		Ip          string
		Score       int
	}
	var scores []*ScoreInfo
	_, err = orm.NewOrm().Raw(sql).QueryRows(&scores)
	if err != nil {
		return nil, err
	}

	s := make(map[int64]map[string]int)
	for _, item := range scores {
		_, exist := s[item.ApplyId]
		if exist == true {
			s[item.ApplyId][item.Ip] = item.Score
		} else {
			tmp := make(map[string]int)
			tmp[item.Ip] = item.Score
			s[item.ApplyId] = tmp
		}
	}

	return s, nil
}

/**
 @Description：自定义一个list，用于服务sort的条件处理
 */
type ApplicationList []*ApplyDetail

/**
 @Description：获取list的len
 @Param:
 @Return：
 */
func (l ApplicationList) Len() int {
	return len(l)
}

/**
 @Description：比较less，按照alert由大到小的顺序处理
 @Param:
 @Return：
 */
func (l ApplicationList) Less(i, j int) bool {
	if l[i].Alert >= l[j].Alert {
		return true
	}

	return false
}

/**
 @Description：执行交换
 @Param:
 @Return：
 */
func (l ApplicationList) Swap(i, j int)  {
	tmp := l[i]
	l[i] = l[j]
	l[j] = tmp
	return
}